{ pkgs, ... }:

let
  passphrase = "supersecret";
  dataDir = "/ran:dom/data";
  subDir = "not_anything_here";
  excludedSubDirFile = "not_this_file_either";
  excludeFile = "not_this_file";
  keepFile = "important_file";
  keepFileData = "important_data";
  localRepo = "/root/back:up";
  localTarRepo = "/root/backup-tar";
  # a repository on a file system which is not mounted automatically
  localRepoMount = "/noAutoMount";
  archiveName = "my_archive";
  remoteRepo = "borg@server:."; # No need to specify path
  privateKey = pkgs.writeText "id_ed25519" ''
    -----BEGIN OPENSSH PRIVATE KEY-----
    b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
    QyNTUxOQAAACBx8UB04Q6Q/fwDFjakHq904PYFzG9pU2TJ9KXpaPMcrwAAAJB+cF5HfnBe
    RwAAAAtzc2gtZWQyNTUxOQAAACBx8UB04Q6Q/fwDFjakHq904PYFzG9pU2TJ9KXpaPMcrw
    AAAEBN75NsJZSpt63faCuaD75Unko0JjlSDxMhYHAPJk2/xXHxQHThDpD9/AMWNqQer3Tg
    9gXMb2lTZMn0pelo8xyvAAAADXJzY2h1ZXR6QGt1cnQ=
    -----END OPENSSH PRIVATE KEY-----
  '';
  publicKey = ''
    ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHHxQHThDpD9/AMWNqQer3Tg9gXMb2lTZMn0pelo8xyv root@client
  '';
  privateKeyAppendOnly = pkgs.writeText "id_ed25519" ''
    -----BEGIN OPENSSH PRIVATE KEY-----
    b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
    QyNTUxOQAAACBacZuz1ELGQdhI7PF6dGFafCDlvh8pSEc4cHjkW0QjLwAAAJC9YTxxvWE8
    cQAAAAtzc2gtZWQyNTUxOQAAACBacZuz1ELGQdhI7PF6dGFafCDlvh8pSEc4cHjkW0QjLw
    AAAEAAhV7wTl5dL/lz+PF/d4PnZXuG1Id6L/mFEiGT1tZsuFpxm7PUQsZB2Ejs8Xp0YVp8
    IOW+HylIRzhweORbRCMvAAAADXJzY2h1ZXR6QGt1cnQ=
    -----END OPENSSH PRIVATE KEY-----
  '';
  publicKeyAppendOnly = ''
    ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFpxm7PUQsZB2Ejs8Xp0YVp8IOW+HylIRzhweORbRCMv root@client
  '';

in
{
  name = "borgbackup";
  meta = with pkgs.lib; {
    maintainers = with maintainers; [ dotlambda ];
  };

  nodes = {
    client =
      { lib, pkgs, ... }:
      {
        virtualisation.fileSystems.${localRepoMount} = {
          device = "tmpfs";
          fsType = "tmpfs";
          options = [ "noauto" ];
        };

        services.borgbackup.jobs = {

          local = {
            paths = dataDir;
            repo = localRepo;
            preHook = ''
              # Don't append a timestamp
              archiveName="${archiveName}"
            '';
            encryption = {
              mode = "repokey";
              inherit passphrase;
            };
            compression = "auto,zlib,9";
            prune.keep = {
              within = "1y";
              yearly = 5;
            };
            exclude = [ "*/${excludeFile}" ];
            extraCreateArgs = [
              "--exclude-caches"
              "--exclude-if-present"
              ".dont backup"
            ];

            wrapper = "borg-main";
            postHook = "echo post";
            startAt = [ ]; # Do not run automatically
          };

          localMount = {
            paths = dataDir;
            repo = localRepoMount;
            encryption.mode = "none";
            wrapper = null;
            startAt = [ ];
          };

          localTar = {
            dumpCommand = pkgs.writeScript "createTarArchive" ''
              ${lib.getExe pkgs.gnutar} cf - ${dataDir}
            '';
            createCommand = "import-tar";
            repo = localTarRepo;
            # Make sure in import-tar mode encryption flags are still respected.
            encryption = {
              mode = "repokey";
              inherit passphrase;
            };
            startAt = [ ]; # Do not run automatically
          };

          remote = {
            paths = dataDir;
            repo = remoteRepo;
            encryption.mode = "none";
            startAt = [ ];
            environment.BORG_RSH = "ssh -oStrictHostKeyChecking=no -i /root/id_ed25519";
          };

          remoteAppendOnly = {
            paths = dataDir;
            repo = remoteRepo;
            encryption.mode = "none";
            startAt = [ ];
            environment.BORG_RSH = "ssh -oStrictHostKeyChecking=no -i /root/id_ed25519.appendOnly";
          };

          commandSuccess = {
            dumpCommand = pkgs.writeScript "commandSuccess" ''
              echo -n test
            '';
            repo = remoteRepo;
            encryption.mode = "none";
            startAt = [ ];
            environment.BORG_RSH = "ssh -oStrictHostKeyChecking=no -i /root/id_ed25519";
          };

          commandFail = {
            dumpCommand = "${pkgs.coreutils}/bin/false";
            repo = remoteRepo;
            encryption.mode = "none";
            startAt = [ ];
            environment.BORG_RSH = "ssh -oStrictHostKeyChecking=no -i /root/id_ed25519";
          };

          sleepInhibited = {
            inhibitsSleep = true;
            # Blocks indefinitely while "backing up" so that we can try to suspend the local system while it's hung
            dumpCommand = pkgs.writeScript "sleepInhibited" ''
              cat /dev/zero
            '';
            repo = remoteRepo;
            encryption.mode = "none";
            startAt = [ ];
            environment.BORG_RSH = "ssh -oStrictHostKeyChecking=no -i /root/id_ed25519";
          };

        };
      };

    server =
      { ... }:
      {
        services.openssh = {
          enable = true;
          settings = {
            PasswordAuthentication = false;
            KbdInteractiveAuthentication = false;
          };
        };

        services.borgbackup.repos.repo1 = {
          authorizedKeys = [ publicKey ];
          path = "/data/borgbackup";
        };

        # Second repo to make sure the authorizedKeys options are merged correctly
        services.borgbackup.repos.repo2 = {
          authorizedKeysAppendOnly = [ publicKeyAppendOnly ];
          path = "/data/borgbackup";
          quota = ".5G";
        };
      };
  };

  testScript = ''
    start_all()

    client.fail('test -d "${remoteRepo}"')

    client.succeed(
        "cp ${privateKey} /root/id_ed25519"
    )
    client.succeed("chmod 0600 /root/id_ed25519")
    client.succeed(
        "cp ${privateKeyAppendOnly} /root/id_ed25519.appendOnly"
    )
    client.succeed("chmod 0600 /root/id_ed25519.appendOnly")

    client.succeed("mkdir -p ${dataDir}/${subDir}")
    client.succeed("touch ${dataDir}/${excludeFile}")
    client.succeed("touch '${dataDir}/${subDir}/.dont backup'")
    client.succeed("touch ${dataDir}/${subDir}/${excludedSubDirFile}")
    client.succeed("echo '${keepFileData}' > ${dataDir}/${keepFile}")

    with subtest("local"):
        borg = "BORG_PASSPHRASE='${passphrase}' borg"
        client.systemctl("start --wait borgbackup-job-local")
        client.fail("systemctl is-failed borgbackup-job-local")
        # Make sure exactly one archive has been created
        assert int(client.succeed("{} list '${localRepo}' | wc -l".format(borg))) > 0
        # Make sure excludeFile has been excluded
        client.fail(
            "{} list '${localRepo}::${archiveName}' | grep -qF '${excludeFile}'".format(borg)
        )
        # Make sure excludedSubDirFile has been excluded
        client.fail(
            "{} list '${localRepo}::${archiveName}' | grep -qF '${subDir}/${excludedSubDirFile}".format(borg)
        )
        # Make sure keepFile has the correct content
        client.succeed("{} extract '${localRepo}::${archiveName}'".format(borg))
        assert "${keepFileData}" in client.succeed("cat ${dataDir}/${keepFile}")
        # Make sure the same is true when using `borg mount`
        client.succeed(
            "mkdir -p /mnt/borg && {} mount '${localRepo}::${archiveName}' /mnt/borg".format(
                borg
            )
        )
        assert "${keepFileData}" in client.succeed(
            "cat /mnt/borg/${dataDir}/${keepFile}"
        )

        # Make sure custom wrapper name works
        client.succeed("command -v borg-main")

    with subtest("localMount"):
        # the file system for the repo should not be already mounted
        client.fail("mount | grep ${localRepoMount}")
        # ensure trying to write to the mountpoint before the fs is mounted fails
        client.succeed("chattr +i ${localRepoMount}")
        borg = "borg"
        client.systemctl("start --wait borgbackup-job-localMount")
        client.fail("systemctl is-failed borgbackup-job-localMount")
        # Make sure exactly one archive has been created
        assert int(client.succeed("{} list '${localRepoMount}' | wc -l".format(borg))) > 0

        # Make sure disabling wrapper works
        client.fail("command -v borg-job-localMount")

    with subtest("localTar"):
        borg = "BORG_PASSPHRASE='${passphrase}' borg"
        client.systemctl("start --wait borgbackup-job-localTar")
        client.fail("systemctl is-failed borgbackup-job-localTar")
        archiveName, = client.succeed("{} list --format '{{archive}}{{NL}}' '${localTarRepo}'".format(borg)).strip().split("\n")
        # Since excludes are not supported by import-tar, we expect to find exclude file, too
        client.succeed(
            "{} list '${localTarRepo}::{}' | grep -qF '${excludeFile}'".format(borg, archiveName)
        )
        # Make sure keepFile has the correct content
        client.succeed("{} extract '${localTarRepo}::{}'".format(borg, archiveName))
        assert "${keepFileData}" in client.succeed("cat ${dataDir}/${keepFile}")

    with subtest("remote"):
        borg = "BORG_RSH='ssh -oStrictHostKeyChecking=no -i /root/id_ed25519' borg"
        server.wait_for_unit("sshd.service")
        client.wait_for_unit("network.target")
        client.systemctl("start --wait borgbackup-job-remote")
        client.fail("systemctl is-failed borgbackup-job-remote")

        # Make sure we can't access repos other than the specified one
        client.fail("{} list borg\@server:wrong".format(borg))

        # Make sure default wrapper works
        client.succeed("command -v borg-job-remote")

        # TODO: Make sure that data is actually deleted

    with subtest("remoteAppendOnly"):
        borg = (
            "BORG_RSH='ssh -oStrictHostKeyChecking=no -i /root/id_ed25519.appendOnly' borg"
        )
        server.wait_for_unit("sshd.service")
        client.wait_for_unit("network.target")
        client.systemctl("start --wait borgbackup-job-remoteAppendOnly")
        client.fail("systemctl is-failed borgbackup-job-remoteAppendOnly")

        # Make sure we can't access repos other than the specified one
        client.fail("{} list borg\@server:wrong".format(borg))

        # TODO: Make sure that data is not actually deleted

    with subtest("commandSuccess"):
        server.wait_for_unit("sshd.service")
        client.wait_for_unit("network.target")
        client.systemctl("start --wait borgbackup-job-commandSuccess")
        client.fail("systemctl is-failed borgbackup-job-commandSuccess")
        id = client.succeed("borg-job-commandSuccess list | tail -n1 | cut -d' ' -f1").strip()
        client.succeed(f"borg-job-commandSuccess extract ::{id} stdin")
        assert "test" == client.succeed("cat stdin")

    with subtest("commandFail"):
        server.wait_for_unit("sshd.service")
        client.wait_for_unit("network.target")
        client.systemctl("start --wait borgbackup-job-commandFail")
        client.succeed("systemctl is-failed borgbackup-job-commandFail")

    with subtest("sleepInhibited"):
        server.wait_for_unit("sshd.service")
        client.wait_for_unit("network.target")
        client.fail("systemd-inhibit --list | grep -q borgbackup")
        client.systemctl("start borgbackup-job-sleepInhibited")
        client.wait_until_succeeds("systemd-inhibit --list | grep -q borgbackup")
        client.systemctl("stop borgbackup-job-sleepInhibited")
  '';
}
